/*
 *PCI enumeration goes here.
 *This code is adapted from Linux source,please do not forget to attribute
 * your credits to Linux developpers, if you want to.
 *Although you can use the code freely, please respect GPL if you want to use this code for other purposes.
 */

#include <asm/io.h>
#include <cnix/types.h>
#include <cnix/kernel.h>
#include <cnix/driver.h>
#include <cnix/errno.h>
#include <cnix/pci.h>
#include <cnix/list.h>
#include <cnix/string.h>
#include <cnix/mm.h>

#define LEGACY_IO_RESOURCE  (IORESOURCE_IO | IORESOURCE_PCI_FIXED)

/*
 * Functions for accessing PCI base (first 256 bytes) and extended
 * (4096 bytes per PCI function) configuration space with type 1
 * accesses.
 */

#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
  (0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
  | (devfn << 8) | (reg & 0xFC))

#define ROOT_PCI_SEG		0x0
#define ROOT_PCI_BUSNR	0x0

#define IO_SPACE_LIMIT 0xffff

static int pci_conf1_read(unsigned int seg, unsigned int bus,
        unsigned int devfn, int reg, int len, u32_t *value)
{
  if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
    *value = -1;
    return -EINVAL;
  }

  outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

  switch (len) {
  case 1:
    *value = inb(0xCFC + (reg & 3));
    break;
  case 2:
    *value = inw(0xCFC + (reg & 2));
    break;
  case 4:
    *value = inl(0xCFC);
    break;
  }

  return 0;
}

static int pci_conf1_write(unsigned int seg, unsigned int bus,
         unsigned int devfn, int reg, int len, u32_t value)
{
  if ((bus > 255) || (devfn > 255) || (reg > 4095))
    return -EINVAL;

  outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

  switch (len) {
  case 1:
    outb((u8_t)value, 0xCFC + (reg & 3));
    break;
  case 2:
    outw((u16_t)value, 0xCFC + (reg & 2));
    break;
  case 4:
    outl((u32_t)value, 0xCFC);
    break;
  }

  return 0;
}

struct pci_raw_ops {
  int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
            int reg, int len, u32_t *val);
  int (*write)(unsigned int domain, unsigned int bus, unsigned int devfn,
            int reg, int len, u32_t val);
};

struct pci_bus *pci_root=NULL;
/*
 *I suppose supporting conf1 is enough for our purpose.
 */
struct pci_raw_ops raw_pci_ops = {
  .read =   pci_conf1_read,
  .write =  pci_conf1_write,
};

/*
 *  Wrappers for all PCI configuration access functions.  They just check
 *  alignment, do locking and call the low-level functions pointed to
 *  by pci_dev->ops.
 */

#define PCI_byte_BAD 0
#define PCI_word_BAD (pos & 1)
#define PCI_dword_BAD (pos & 3)

#define PCI_OP_READ(size,type,len) \
int pci_read_config_##size \
  (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
{                 \
  int res;              \
  u32_t data = 0;             \
  if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
  res = raw_pci_ops.read(bus->domain,bus->busnr,devfn, pos, len, &data);    \
  *value = (type)data;            \
  return res;             \
}

#define PCI_OP_WRITE(size,type,len) \
int pci_write_config_##size \
  (struct pci_bus *bus, unsigned int devfn, int pos, type value)  \
{\
  int res;\
  if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
  res = raw_pci_ops.write(bus->domain,bus->busnr,devfn, pos, len, value);   \
  return res;             \
}           

PCI_OP_READ(byte,u8_t,1)
PCI_OP_READ(word, u16_t, 2)
PCI_OP_READ(dword, u32_t, 4) 
PCI_OP_WRITE(byte, u8_t, 1)
PCI_OP_WRITE(word, u16_t, 2)
PCI_OP_WRITE(dword, u32_t, 4)

static int pci_sanity_check(struct pci_raw_ops *o)
{
  u32_t x = 0;
  int devfn;

  for (devfn = 0; devfn < 0x100; devfn++) {
    if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))
      continue;
    if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
      return 1;

    if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
      continue;
    if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
      return 1;
  }

  printk("PCI: Sanity check failed\n");
  return 0;
}

static int pci_check_type1(void)
{
  unsigned int tmp;
  int works = 0;

  outb(0x01, 0xCFB);
  tmp = inl(0xCF8);
  outl(0x80000000, 0xCF8);
  if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&raw_pci_ops)) {
    works = 1;
		printk("pci conf1 works!\n");
  }
  outl(tmp, 0xCF8);

  return works;
}

static struct pci_device *alloc_pci_dev(void)
{
	struct pci_device *dev=NULL;
	
	dev = (struct pci_device*)kmalloc(sizeof(struct pci_device),0);
	if(!dev){
		printk("can not allocate memory for pci_device in func: %s of file: %s\n",
				__func__,__FILE__);
		while(1);
	}

	memset(dev,0,sizeof(struct pci_device));
	list_head_init(&dev->dev_node);

	return dev;
}

static void init_pci_root()
{
	pci_root = (struct pci_bus*)kmalloc(sizeof(struct pci_bus),0);
	if(!pci_root){
		printk("can not allocate memory for pci_root.\n");
		while(1);
	}

	pci_root->busnr = ROOT_PCI_BUSNR;
	//sprintf(pci_root->bus_name,"pci: %s:%s",ROOT_PCI_SEG,ROOT_PCI_BUSNR);
	strcpy(pci_root->bus_name,"pci: 0000:00");
	pci_root->domain = ROOT_PCI_SEG;
	pci_root->parent=NULL;
	pci_root->children=NULL;
	pci_root->sibling=NULL;
	list_head_init(&pci_root->devices);
	printk("pci_root initialized.\n");
}

static struct pci_device *pci_get_slot(struct pci_bus *bus,unsigned int devfn)
{
	struct list_struct *tmp=NULL;
	struct pci_device *dev;

	list_foreach_quick(tmp,&bus->devices){
		if(tmp){
			dev = (struct pci_device *)tmp;
			if(dev->devfn == devfn)
				return dev;
		}
	}

	dev = NULL;
	return dev;
}

static void msleep(int d)
{
	unsigned int b = 0xbcdef;
	b *= d;
	while(b--);
}

static int pci_cfg_space_size_ext(struct pci_device *dev)
{
  u32_t status;
  int pos = PCI_CFG_SPACE_SIZE;

  if (pci_read_config_dword(dev->bus, dev->devfn,pos, &status) != PCIBIOS_SUCCESSFUL)
    goto fail;
  if (status == 0xffffffff)
    goto fail;

  return PCI_CFG_SPACE_EXP_SIZE;

 fail:
  return PCI_CFG_SPACE_SIZE;
}

static int __pci_bus_find_cap_start(struct pci_bus *bus,
            unsigned int devfn, u8_t hdr_type)
{
  u16_t status;

  pci_read_config_word(bus, devfn, PCI_STATUS, &status);
  if (!(status & PCI_STATUS_CAP_LIST))
    return 0;

  switch (hdr_type) {
  case PCI_HEADER_TYPE_NORMAL:
  case PCI_HEADER_TYPE_BRIDGE:
    return PCI_CAPABILITY_LIST;
  case PCI_HEADER_TYPE_CARDBUS:
    return PCI_CB_CAPABILITY_LIST;
  default:
    return 0;
  }

  return 0;
}

static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
           u8_t pos, int cap, int *ttl)
{
  u8_t id;

  while ((*ttl)--) {
    pci_read_config_byte(bus, devfn, pos, &pos);
    if (pos < 0x40)
      break;
    pos &= ~3;
    pci_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID,
           &id);
    if (id == 0xff)
      break;
    if (id == cap)
      return pos;
    pos += PCI_CAP_LIST_NEXT;
  }
  return 0;
}

static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn,
             u8_t pos, int cap)
{
	#define PCI_FIND_CAP_TTL  48
  int ttl = PCI_FIND_CAP_TTL;

  return __pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl);
}


/**
 * pci_find_capability - query for devices' capabilities 
 * @dev: PCI device to query
 * @cap: capability code
 *
 * Tell if a device supports a given PCI capability.
 * Returns the address of the requested capability structure within the
 * device's PCI configuration space or 0 in case the device does not
 * support it.  Possible values for @cap:
 *
 *  %PCI_CAP_ID_PM           Power Management 
 *  %PCI_CAP_ID_AGP          Accelerated Graphics Port 
 *  %PCI_CAP_ID_VPD          Vital Product Data 
 *  %PCI_CAP_ID_SLOTID       Slot Identification 
 *  %PCI_CAP_ID_MSI          Message Signalled Interrupts
 *  %PCI_CAP_ID_CHSWP        CompactPCI HotSwap 
 *  %PCI_CAP_ID_PCIX         PCI-X
 *  %PCI_CAP_ID_EXP          PCI Express
 */
int pci_find_capability(struct pci_device *dev, int cap)
{
  int pos;

  pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type);
  if (pos)
    pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);

  return pos;
}

static int pci_cfg_space_size(struct pci_device *dev)
{
  int pos;
  u32_t status;
  u16_t class;

  class = dev->class >> 8;
  if (class == PCI_CLASS_BRIDGE_HOST)
    return pci_cfg_space_size_ext(dev);

  //pos = pci_pcie_cap(dev);
	pos = dev->pcie_cap;
  if (!pos) {
    pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
    if (!pos)
      goto fail;

    pci_read_config_dword(dev->bus, dev->devfn,pos + PCI_X_STATUS, &status);
    if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ)))
      goto fail;
  }

  return pci_cfg_space_size_ext(dev);

 fail:
  return PCI_CFG_SPACE_SIZE;
}

void set_pcie_port_type(struct pci_device *pdev)
{
  int pos;
  u16_t reg16=0;

  pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
  if (!pos)
    return;
  pdev->is_pcie = 1;
  pdev->pcie_cap = pos;
  pci_read_config_word(pdev->bus,pdev->devfn, pos + PCI_EXP_FLAGS, &reg16);
  pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
}

/*
 * Translate the low bits of the PCI base
 * to the resource type
 */
static inline unsigned int pci_calc_resource_flags(unsigned int flags)
{
  if (flags & PCI_BASE_ADDRESS_SPACE_IO)
    return IORESOURCE_IO;

  if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
    return IORESOURCE_MEM | IORESOURCE_PREFETCH;

  return IORESOURCE_MEM;
}

static u64_t pci_size(u64_t base, u64_t maxbase, u64_t mask)
{
  u64_t size = mask & maxbase;  /* Find the significant bits */
  if (!size)
    return 0;

  /* Get the lowest of them to find the decode size, and
     from that the extent.  */
  size = (size & ~(size-1)) - 1;

  /* base == maxbase can be valid only if the BAR has
     already been programmed with all 1s.  */
  if (base == maxbase && ((base | size) & mask) != mask)
    return 0;

  return size;
}

static inline enum pci_bar_type decode_bar(struct resource *res, u32_t bar)
{ 
  if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
    res->flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
    return pci_bar_io;
  }

  res->flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;
  
  if (res->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
    return pci_bar_mem64;
  return pci_bar_mem32;
}

/**
 * pci_read_base - read a PCI BAR
 * @dev: the PCI device
 * @type: type of the BAR
 * @res: resource buffer to be filled in
 * @pos: BAR position in the config space
 *
 * Returns 1 if the BAR is 64-bit, or 0 if 32-bit.
 */
int __pci_read_base(struct pci_device *dev, enum pci_bar_type type,
      struct resource *res, unsigned int pos)
{
  u32_t l, sz, mask;

  mask = type ? PCI_ROM_ADDRESS_MASK : ~0;

  res->name = dev->dev_name;

  pci_read_config_dword(dev->bus, dev->devfn,pos, &l);
  pci_write_config_dword(dev->bus,dev->devfn, pos, l | mask);
  pci_read_config_dword(dev->bus, dev->devfn,pos, &sz);
  pci_write_config_dword(dev->bus,dev->devfn, pos, l);

  /*
   * All bits set in sz means the device isn't working properly.
   * If the BAR isn't implemented, all bits must be 0.  If it's a
   * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
   * 1 must be clear.
   */
  if (!sz || sz == 0xffffffff)
    goto fail;

  /*
   * I don't know how l can have all bits set.  Copied from old code.
   * Maybe it fixes a bug on some ancient platform.
   */
  if (l == 0xffffffff)
    l = 0;
  if (type == pci_bar_unknown) {
    type = decode_bar(res, l);
    res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
    if (type == pci_bar_io) {
      l &= PCI_BASE_ADDRESS_IO_MASK;
      mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT;
    } else {
      l &= PCI_BASE_ADDRESS_MEM_MASK;
      mask = (u32_t)PCI_BASE_ADDRESS_MEM_MASK;
    }
  } else {
    res->flags |= (l & IORESOURCE_ROM_ENABLE);
    l &= PCI_ROM_ADDRESS_MASK;
    mask = (u32_t)PCI_ROM_ADDRESS_MASK;
  }

  if (type == pci_bar_mem64) {
    u64_t l64 = l;
    u64_t sz64 = sz;
    u64_t mask64 = mask | (u64_t)~0 << 32;

    pci_read_config_dword(dev->bus, dev->devfn,pos + 4, &l);
    pci_write_config_dword(dev->bus,dev->devfn, pos + 4, ~0);
    pci_read_config_dword(dev->bus, dev->devfn,pos + 4, &sz);
    pci_write_config_dword(dev->bus,dev->devfn, pos + 4, l);

    l64 |= ((u64_t)l << 32);
    sz64 |= ((u64_t)sz << 32);

    sz64 = pci_size(l64, sz64, mask64);

    if (!sz64)
      goto fail;

    if (/*(sizeof(resource_size_t) < 8) && */(sz64 > 0x100000000ULL)) {
      printk("reg %x: can't handle 64-bit BAR\n",pos);
      goto fail;
    }
    res->flags |= IORESOURCE_MEM_64;
    if (/*(sizeof(resource_size_t) < 8) && */l) {
      /* Address above 32-bit boundary; disable the BAR */
      pci_write_config_dword(dev->bus,dev->devfn, pos, 0);
      pci_write_config_dword(dev->bus,dev->devfn, pos + 4, 0);
      res->start = 0;
      res->end = sz64;
    } else {
      res->start = l64;
      res->end = l64 + sz64;
    }
  } else {
    sz = pci_size(l, sz, mask);

    if (!sz)
      goto fail;

    res->start = l;
    res->end = l + sz;
  }

 out:
  return (type == pci_bar_mem64) ? 1 : 0;
 fail:
  res->flags = 0;
  goto out;
}

static void pci_read_bases(struct pci_device *dev, unsigned int howmany, int rom)
{
  unsigned int pos, reg;

  for (pos = 0; pos < howmany; pos++) {
    struct resource *res = &dev->resource[pos];
    reg = PCI_BASE_ADDRESS_0 + (pos << 2);
    pos += __pci_read_base(dev, pci_bar_unknown, res, reg);
  }

  if (rom) {
    struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
    dev->rom_base_reg = rom;
    res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |
        IORESOURCE_READONLY | IORESOURCE_CACHEABLE |
        IORESOURCE_SIZEALIGN;
    __pci_read_base(dev, pci_bar_mem32, res, rom);
  }
}

/*
 * Read interrupt line and base address registers.
 * The architecture-dependent code can tweak these, of course.
 */
static void pci_read_irq(struct pci_device *dev)
{
  unsigned char irq;

  pci_read_config_byte(dev->bus,dev->devfn,PCI_INTERRUPT_PIN, &irq);
  dev->pin = irq;
  if (irq)
    pci_read_config_byte(dev->bus,dev->devfn,PCI_INTERRUPT_LINE, &irq);
  dev->irq = irq;
	//printk("vendor: %x device: %x pin: %d irq: %d\n",(int)dev->vendor,(int)dev->device,(int)dev->pin,(int)dev->irq);
}

void set_pcie_hotplug_bridge(struct pci_device *pdev)
{
  int pos;
  u16_t reg16=0;
  u32_t reg32=0;

  //pos = pci_pcie_cap(pdev);
	pos = pdev->pcie_cap;
  if (!pos)
    return;
  pci_read_config_word(pdev->bus,pdev->devfn,pos + PCI_EXP_FLAGS, &reg16);
  if (!(reg16 & PCI_EXP_FLAGS_SLOT))
    return;
  pci_read_config_dword(pdev->bus, pdev->devfn,pos + PCI_EXP_SLTCAP, &reg32);
  if (reg32 & PCI_EXP_SLTCAP_HPC)
    pdev->is_hotplug_bridge = 1;
}

static int pci_setup_device(struct pci_device *dev)
{
	u8_t hdr_type;
	u32_t class;
	int pos;

	if (pci_read_config_byte(dev->bus,dev->devfn,PCI_HEADER_TYPE, &hdr_type))
		return -EIO;

	dev->hdr_type = hdr_type&0x7f;
	dev->multifunction = !!(hdr_type & 0x80);
	
	sprintf(dev->dev_name,"%04x:%02x:%02x.%d\n",dev->bus->domain,
			dev->bus->busnr,PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));

  pci_read_config_dword(dev->bus, dev->devfn,PCI_CLASS_REVISION, &class);
  dev->revision = class & 0xff;
  class >>= 8;            /* upper 3 bytes */
  dev->class = class;
  class >>= 8;

#if 0
  printk("found [%04x:%02x:%02x.%d] class %06x header type %02x\n",
     dev->bus->domain, dev->bus->busnr,PCI_SLOT(dev->devfn),
		 PCI_FUNC(dev->devfn),class, dev->hdr_type);
#endif

  /* need to have dev->class ready */
  dev->cfg_size = pci_cfg_space_size(dev);

  /* Early fixups, before probing the BARs */
  //pci_fixup_device(pci_fixup_early, dev);

  /* device class may be changed after fixup */
  class = dev->class >> 8;	

	switch(dev->hdr_type){
	case PCI_HEADER_TYPE_NORMAL:        /* standard header */
		if(class == PCI_CLASS_BRIDGE_PCI){
			printk("hdr_type and class are not consistent. %s:%s\n",__func__,__FILE__);
			while(1);
		}
    pci_read_irq(dev);
    pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
    pci_read_config_word(dev->bus,dev->devfn,PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
    pci_read_config_word(dev->bus,dev->devfn,PCI_SUBSYSTEM_ID, &dev->subsystem_device);

    /*
     *  Do the ugly legacy mode stuff here rather than broken chip
     *  quirk code. Legacy mode ATA controllers have fixed
     *  addresses. These are not always echoed in BAR0-3, and
     *  BAR0-3 in a few cases contain junk!
     */
    if (class == PCI_CLASS_STORAGE_IDE) {
      u8_t progif;
      pci_read_config_byte(dev->bus,dev->devfn,PCI_CLASS_PROG, &progif);
      if ((progif & 1) == 0) {
        dev->resource[0].start = 0x1F0;
        dev->resource[0].end = 0x1F7;
        dev->resource[0].flags = LEGACY_IO_RESOURCE;
        dev->resource[1].start = 0x3F6;
        dev->resource[1].end = 0x3F6;
        dev->resource[1].flags = LEGACY_IO_RESOURCE;
      }
      if ((progif & 4) == 0) {
        dev->resource[2].start = 0x170;
        dev->resource[2].end = 0x177;
        dev->resource[2].flags = LEGACY_IO_RESOURCE;
        dev->resource[3].start = 0x376;
        dev->resource[3].end = 0x376;
        dev->resource[3].flags = LEGACY_IO_RESOURCE;
      }
    }
    break;
  case PCI_HEADER_TYPE_BRIDGE:        /* bridge header */
    if (class != PCI_CLASS_BRIDGE_PCI){
      printk("class inconsistent.\n");
			while(1);
		}
    /* The PCI-to-PCI bridge spec requires that subtractive
       decoding (i.e. transparent) bridge must have programming
       interface code of 0x01. */
    pci_read_irq(dev);
    dev->transparent = ((dev->class & 0xff) == 1);
    pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
    set_pcie_hotplug_bridge(dev);
    pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
    if (pos) {
      pci_read_config_word(dev->bus,dev->devfn, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
      pci_read_config_word(dev->bus,dev->devfn, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
    }
    break;

  case PCI_HEADER_TYPE_CARDBUS:       /* CardBus bridge header */
    if (class != PCI_CLASS_BRIDGE_CARDBUS){
      printk("pci dev class inconsistent.\n");
			while(1);
		}
    pci_read_irq(dev);
    pci_read_bases(dev, 1, 0);
    pci_read_config_word(dev->bus, dev->devfn,PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
    pci_read_config_word(dev->bus,dev->devfn, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
    break;

  default:            /* unknown header */
    printk("unknown header type %02x, "
      "ignoring device\n", dev->hdr_type);
    return -EIO;
	}

	//done
	return 0;
}

static struct pci_device * pci_scan_device(struct pci_bus *bus, unsigned int devfn)
{
  struct pci_device *dev;
  u32_t l;
  int delay = 1;

  if (pci_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
    return NULL;

  /* some broken boards return 0 or ~0 if a slot is empty: */
  if (l == 0xffffffff || l == 0x00000000 ||
      l == 0x0000ffff || l == 0xffff0000)
    return NULL;

  /* Configuration request Retry Status */
  while (l == 0xffff0001) {
    msleep(delay);
    delay *= 2;
    if (pci_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
      return NULL;
    /* Card hasn't responded in 60 seconds?  Must be stuck. */
    if (delay > 60 * 1000) {
      printk("pci %04x:%02x:%02x.%d: not "
          "responding\n", bus->domain,
          bus->busnr, PCI_SLOT(devfn),
          PCI_FUNC(devfn));
      return NULL;
    }
  }

  dev = alloc_pci_dev();
  if (!dev)
    return NULL;

  dev->bus = bus;
  dev->devfn = devfn;
  dev->vendor = l & 0xffff;
  dev->device = (l >> 16) & 0xffff;

  if (pci_setup_device(dev)) {
		printk("Can not setup the PCI device. Giving up ...\n");
    kfree(dev);
    return NULL;
  }

  return dev;
}

void pci_device_add(struct pci_device *dev, struct pci_bus *bus)
{
  /*
   * Add the device to our list of discovered devices
   * and the bus list for fixup functions, etc.
   */
  list_add_tail(&bus->devices,&dev->dev_node);
}

static struct pci_device *pci_scan_single_device(struct pci_bus *bus,unsigned int devfn)
{
	struct pci_device *dev;
	
	dev = pci_get_slot(bus,devfn);
	if(dev) {
		printk("There are ghosts on this motherborad.\n");
		return dev;
	}

	dev = pci_scan_device(bus,devfn);
	if(!dev)
		return NULL;

	pci_device_add(dev,bus);

	return dev;
}

static void pci_scan_slot(struct pci_bus *bus, unsigned int devfn)
{
	int fn;
	struct pci_device *dev;

	dev = pci_scan_single_device(bus,devfn);

	if(dev && dev->multifunction) {
		for(fn=1;fn<8;fn++){
			dev = pci_scan_single_device(bus,devfn+fn);
			if(dev)
				dev->multifunction=1;
		}
	}	
}

static void pci_scan_child_bus(struct pci_bus *bus)
{
	unsigned int devfn;

	/* scan all slots on this bus */
	for(devfn=0;devfn<0x100;devfn+=8)
		pci_scan_slot(bus,devfn);

#if 0
	/* scan all PCI-PCI bridges on this bus*/
	for(pass=0;pass<2;pass++)
		list_foreach_quick(tmp,&bus->devices){
			dev = (struct pci_device*)tmp;
			if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
				 dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
				pci_scan_bridge(bus,dev,pass);
	}
#endif
}

struct pci_device * find_dev_by_vendor(unsigned short vendor,unsigned short device)
{
	struct list_struct *tmp=NULL;
	struct pci_device *pdev=NULL;

	list_foreach_quick(tmp,&pci_root->devices){
		pdev = (struct pci_device*)tmp;
		if(pdev->vendor == vendor &&
			 pdev->device == device){
			return pdev;
		}
	}

	return NULL;
}

struct pci_device * find_dev(unsigned short domain,unsigned char busnr,unsigned int devfn)
{
	struct list_struct *tmp;
	struct pci_device *pdev=NULL;

	if(pci_root->domain != domain ||
		 pci_root->busnr != busnr){
		printk("domain or busnr not match in find_bus.\n");
		while(1);
	}

	list_foreach_quick(tmp,&pci_root->devices){
		pdev = (struct pci_device*)tmp;
		if(pdev->devfn==devfn){
/*
  		printk("find_dev [%04x:%02x:%02x.%d] class %06x header type %02x\n",
     		pdev->bus->domain, pdev->bus->busnr,PCI_SLOT(pdev->devfn),
		 		PCI_FUNC(pdev->devfn),pdev->class, pdev->hdr_type);
*/
			return pdev;
		}
	}

	return NULL;
}

void pci_bus_scan(void)
{
	if(!pci_check_type1())
		printk("Checking pci_conf1 failed.\n");

	init_pci_root();
	printk("scanning root bus ...\n");
	pci_scan_child_bus(pci_root);
}

unsigned char cfgspace[256];

int pci_ide_detect(void)
{
	unsigned long odata, idata;
	int bus, dev, func, index;
	unsigned long * lcs = (unsigned long *)cfgspace;
	unsigned short * scs = (unsigned short *)&cfgspace[0x0a];

	for(bus = 0; bus < 256; bus++){
		for(dev = 0; dev < 32; dev++){
			for(func = 0; func < 8; func++){
				for(index = 0; index < 64; index++){
					odata = 1 << 31;
					odata |= bus << 16;
					odata |= dev << 11;
					odata |= func << 8;
					odata |= index << 2;

					outl(odata, 0x0cf8);
					idata = inl(0x0cfc);

					lcs[index] = idata;
				}

				/*
				 * base-class: 0x01, mass storage controller
				 * sub-class:
				 * 	0x00 scsi
				 * 	0x01 ide
				 * 	0x02 floppy
				 */
				if(*scs == 0x0101) 
					goto found;

				/* mutli func */
				if(!func && !(cfgspace[0x0e] & 0x80))
					break;
			}
		}
	}

	return 0;

found:

#if 0
	{
		int i, j;

		printk("\n");
		for(i = 0; i < 16; i++){
			for(j = 0; j < 16; j++)
				printk("%x ", cfgspace[i * 16 + j]);
			printk("\n");
		}
	}
#endif

	return 1;
}
